using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
namespace PlayTime
{
    public enum HandState { Deal, Auction, Play, Score };
     
    public class Hand
    {
        private const int nMaxTrickPerHand = 13;
        public delegate void StateChangeDelategate(object state);
        public event StateChangeDelategate eventStateChange;
        ArrayList[] m_cCallHistoryByPlayer;
        
        private Session m_cSession;
        private int m_nConsecutivePassCount;
        
        private HandState m_eHandState;
        private Position m_eCurCallerPos;
        private Call m_cTopCall = null;
        private Call m_cLastCall = null;
        private bool m_bCallDoubled = false;
        private bool m_bCallReDoubled = false;
        
        private Call m_cContract = null;
        private BridgePlayer m_cDeclarer = null;
        private BridgePlayer m_cDummpy = null;
        private BridgePlayer m_cOpenningLeader = null;
        private Trick m_cCurrentTrick = null;
        private int m_nTrickCompleted;
        public Hand(Session session)
        {
            m_cSession = session;
            StateChangeDelategate aDelegate = new StateChangeDelategate(m_cSession.m_ctableDisplay.eventStateChange);
            eventStateChange += aDelegate;
            m_cCallHistoryByPlayer = new ArrayList[4];
            m_cCallHistoryByPlayer[0] = new ArrayList();
            m_cCallHistoryByPlayer[1] = new ArrayList();
            m_cCallHistoryByPlayer[2] = new ArrayList();
            m_cCallHistoryByPlayer[3] = new ArrayList();
            State = HandState.Deal;
            m_nTrickCompleted = 0;
            
        }

        internal void Play()
        {
            //m_cCurrentTrick = new Trick();
        }
        
       
        public Trick Trick
        {
            get { return m_cCurrentTrick; }
            set { m_cCurrentTrick = value; }
        }
        internal TrickStatus Lead(Card card, BridgePlayer player)
        {
            TrickStatus eTrickStatus = TrickStatus.CurrentTrickInComplete;
            bool bIsValidLead = isValidLead(card, player);
            if (bIsValidLead == true)
            {
                player.Lead(card);
                Trick.PassCard(card, player.Position,m_cContract);
                eTrickStatus = Trick.TrickStatus;
              
                if (eTrickStatus == TrickStatus.CurrentTrickComplete)
                {                   
                    ++m_nTrickCompleted;
                    if (m_nTrickCompleted == nMaxTrickPerHand)
                    {
                        eTrickStatus = TrickStatus.AllTrickCompleted;
                        SetHandScore();
                    }
                    else
                    {                        
                        Position winPos = Trick.Winner;
                        BridgePlayer tricWinner = Session.getPlayer(winPos);
                        tricWinner.AddTrick(Trick);
                        //PartnerShip winnerPartnerShip = Session.getPartnerShip(winPos);
                        //winnerPartnerShip.AddTrick(Trick);
                        Trick = new Trick();
                        Trick.NextPositionToLead = winPos;
                    }
                }
                else if (eTrickStatus == TrickStatus.CurrentTrickInComplete)
                {
                    Trick.NextPositionToLead = getNextPostion(player.Position);
                }                
            }
            else 
            {
                eTrickStatus = TrickStatus .CurrentTrickInComplete;
            }
            return eTrickStatus;
        }

        private void SetHandScore()
        {
            throw new Exception("The method or operation is not implemented.");
        }
        internal bool isValidLead(Card card, BridgePlayer player)
        {
            bool bValidLead = false;
            if (Trick == null)
            {
                return bValidLead = false;
            }
            if (Trick.CardPassed == 0)
            {
                bValidLead = true;
            }
            else if (Trick.LeadSuit == card.CardSuit)
            {
                bValidLead = true;
            }
            else if ((Trick.LeadSuit != card.CardSuit) && ( false == player.Contains(Trick.LeadSuit))) 
            {
                bValidLead = true;
            }
            return bValidLead ;
        }

        public Session Session
        {
            get { return m_cSession; }
        }

        public HandState State
        {
            get { return m_eHandState; }
            set
            {
                m_eHandState = value;
                eventStateChange(m_eHandState);
            }
        }

        internal void DealCards()
        {
            m_cSession.Dealer.DealCards(m_cSession, this);

            State = HandState.Auction;
            CurrentCaller = m_cSession.Dealer.Position;
        }

        public Position CurrentCaller
        {
            get { return m_eCurCallerPos; }
            set
            {
                m_eCurCallerPos = value;
            }
            
        }

        public  Position getNextPostion(Position pos)
        {
            Position retPos;
            if (pos == Position.West)
            {
                retPos = Position.North;
            }
            else
            {
                retPos = ++pos;
            }
            return retPos;
        }

        public Position getPreviousPostion(Position pos)
        {
            
            Position retPos;
            if (pos == Position.North)
            {
                retPos = Position.West;
            }
            else
            {
                retPos = --pos;
            }
            return retPos;
        }
        internal void Auction()
        {
            ContractBridgeCallDisplay callDispForm = new ContractBridgeCallDisplay(this);
            callDispForm.ShowDialog();
        }

        public BridgePlayer Caller
        {
            get { return m_cSession.getPlayer(m_eCurCallerPos); }
        }
        public Position Declarer
        {
            get { return m_cDeclarer.Position; }
        }
        public Call TopCall
        {
            get { return m_cTopCall; }
        }
        internal CallStatus ProcessCall(Call curCall, string callStatus)
        {
            CallStatus callStat = CallStatus.InvalidCall;
          
            if (m_cTopCall == null)
            {
                m_cTopCall = curCall;
            }
            if (IsValidCall(curCall) == false)
            {
                callStatus = "Invalid call";
                return callStat = CallStatus.InvalidCall;
            }

            switch (curCall.CallType)
            {
                case CallType.Pass:
                    callStat = ProcessPassCall(curCall, callStatus);
                    break;
                case CallType.Double:
                    m_nConsecutivePassCount = 0;
                    callStat = ProcessDoubleCall(curCall, callStatus);
                    break;
                case CallType.ReDouble:
                    m_nConsecutivePassCount = 0;
                    callStat = ProcessReDoubleCall(curCall, callStatus);
                    break;
                default:
                    m_nConsecutivePassCount = 0;
                    callStat = ProcessNormalBid(curCall, callStatus);
                    break;
            }
           
            if ((callStat == CallStatus.CallIMidway) || 
                (callStat == CallStatus.CallComplete)
                )
            {
                Caller.InsertBid(curCall);
                InsertBid(curCall, m_eCurCallerPos);
                if (curCall.CallType != CallType.Pass)
                {
                    m_cLastCall = curCall;
                }
                if (curCall > m_cTopCall)
                {
                    m_cTopCall = curCall;
                }
                if (callStat == CallStatus.CallIMidway)
                {
                    m_eCurCallerPos = getNextPostion(Caller.Position);
                }
                if (callStat == CallStatus.CallComplete)
                {
                    m_cContract = m_cTopCall;
                    m_cDeclarer = getDeclarer();
                    m_cDummpy = getPartner(m_cDeclarer.Position);
                    m_cOpenningLeader = getOpeningLeader(m_cDummpy);                    
                    NewTrick(m_cOpenningLeader.Position);
                    State = HandState.Play;
                }
            }
            return callStat;
        }

        private void NewTrick(Position position)
        {
            Trick = new Trick();
            Trick.NextPositionToLead = position;            
        }
        public Position OpeningLead
        {
            get { return m_cOpenningLeader.Position; }
            //set { }
        }
        private BridgePlayer getOpeningLeader(BridgePlayer m_cDummpy)
        {
            return Session.getPlayer(getPreviousPostion(m_cDummpy.Position));            
        }

        private BridgePlayer getPartner(Position position)
        {
            return Session.getPlayer(getPartnerPosition(position));
        }

        public Position getPartnerPosition(Position position)
        {
            Position pos = Position.North;
            switch (position)
            {
                case Position.East:
                    pos = Position.West;
                    break;
                case Position.West:
                    pos = Position.East;
                    break;
                case Position.South:
                    pos = Position.North;
                    break;
                case Position.North:
                    pos = Position.South;
                    break;
            }
            return pos;
        }

        private BridgePlayer getDeclarer()
        {
            Position topCallerPos = m_cTopCall.Caller;
            BridgePlayer player = Session.getPlayer(topCallerPos);
            int n1 = FindFirstCallIndex(topCallerPos, m_cTopCall.BidDenomination);
            Position topCallerPartner = getPartnerPosition(topCallerPos);
            int n2 = FindFirstCallIndex(topCallerPartner, m_cTopCall.BidDenomination);
            if ((n2!= -1)&& (n2 < n1))
            {
                player = Session.getPlayer(topCallerPartner);
            }
            else if (n1 == n2)
            {
                if (m_cCallHistoryByPlayer[(int)topCallerPartner].Count > m_cCallHistoryByPlayer[(int)topCallerPos].Count)
                {
                    player = Session.getPlayer(topCallerPartner);
                }
            }
            return player;
        }
        private int FindFirstCallIndex(Position pos, BidDenomination bd)
        {
            int index = -1;
            ArrayList arr = m_cCallHistoryByPlayer[(int)pos];
            for (int i = 0; i < arr.Count; i++)
            {
                Call call  = (Call)arr[i];
                if (call.BidDenomination == bd)
                {
                    return index = i;
                }
            }
            return index;
        }
        private void InsertBid(Call curCall, Position pos)
        {            
            m_cCallHistoryByPlayer[(int)pos].Add(curCall);
        }

        private CallStatus ProcessNormalBid(Call curCall, string callStatus)
        {             
            m_bCallDoubled = false;
            m_bCallReDoubled = false;
            return CallStatus.CallIMidway;
        }

        private CallStatus ProcessReDoubleCall(Call curCall, string callStatus)
        {
            CallStatus callStatusRet = CallStatus.InvalidCall;
            BridgePlayer player = Session.getPlayer(curCall.Caller);
            if ((m_cLastCall != null) && (m_cLastCall.CallType == CallType.Double) && player.IsOpponent(m_cLastCall.Caller))
            {
                callStatusRet = CallStatus.CallIMidway;
                m_bCallReDoubled = true;
                m_bCallDoubled = false;
            }
            return callStatusRet;
        }

        private CallStatus ProcessDoubleCall(Call curCall, string callStatus)
        {
            CallStatus callStatusRet = CallStatus.InvalidCall;
            BridgePlayer player = Session.getPlayer(curCall.Caller);
            if ((m_cLastCall != null) && (m_cLastCall.CallType == CallType.Bid) && player.IsOpponent(m_cLastCall.Caller))
            {
                callStatusRet = CallStatus.CallIMidway;
                m_bCallDoubled = true;
                
            }
            return callStatusRet;
        }

        private CallStatus ProcessPassCall(Call curCall, string callStatus)
        {
            m_nConsecutivePassCount++;

            if (m_nConsecutivePassCount == 3)
            {
                if (m_cTopCall.CallType != CallType.Pass)
                {
                    return CallStatus.CallComplete;
                }
            }
            else if (m_nConsecutivePassCount == 4)
            {
                return CallStatus.BidPassedOut;
            }
            return CallStatus.CallIMidway;
        }

        private bool IsValidCall(Call curCall)
        {
            bool bRetStat = false;
            if ((curCall == m_cTopCall) && (Caller.Position == m_cSession.Dealer.Position))
            {
                return bRetStat = true;
            }
            if (curCall.CallType == CallType.Bid)
            {
                if (m_cTopCall.CallType == CallType.Pass)
                {
                    return bRetStat = true;
                }
                else if ((curCall > m_cTopCall))
                {
                    return bRetStat = true;
                }

            }
            else if (curCall.CallType == CallType.Pass)//++
            {
                return bRetStat = true;
            }
            else if (curCall.CallType == CallType.Double)
            {
                BridgePlayer player = Session.getPlayer(curCall.Caller);
                if ((m_cLastCall!=null)&&(m_cLastCall.CallType == CallType.Bid) && player.IsOpponent(m_cLastCall.Caller))
                {
                    return bRetStat = true;
                }
                return bRetStat = false;
            }
            else if (curCall.CallType == CallType.ReDouble)
            {
                BridgePlayer player = Session.getPlayer(curCall.Caller);
                if ((m_cLastCall != null) && (m_cLastCall.CallType == CallType.Double) && player.IsOpponent(m_cLastCall.Caller))
                {
                    return bRetStat = true;
                }
                return bRetStat = false;
            }
            return bRetStat;
        }

        internal ArrayList GetCallListByPlayer(Position pos)
        {
            return m_cCallHistoryByPlayer[(int)pos];
        }
        ~Hand()
        {
        }

        internal void PassedOut()
        {         
            foreach (Position pos in Position.GetValues(typeof(Position)))
            {
                BridgePlayer player = Session.getPlayer(pos);
                for (int i = 0; i < nMaxTrickPerHand; i++)
                {
                    Card draw = player.GetBackCard();
                    m_cSession.Deck.ReturnCardToDeck(draw);
                }
            }
            m_cSession.Deal();
        }        
    }
}